package com.king.gkrpc.server;

import com.king.gkrpc.Request;
import com.king.gkrpc.Response;
import com.king.gkrpc.codec.Decoder;
import com.king.gkrpc.codec.Encoder;
import com.king.gkrpc.common.utils.ReflectionUtils;
import com.king.gkrpc.transport.RequestHandler;
import com.king.gkrpc.transport.TransportServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

@Slf4j
public class RpcServer {
    private RpcServerConfig config;
    private TransportServer net;
    private Encoder encoder;
    private Decoder decoder;
    private ServiceManager serviceManager;
    private ServiceInvoker serviceInvoker;

    public RpcServer(){
        this(new RpcServerConfig());
    }

    public RpcServer(RpcServerConfig config){
        this.config = config;

        this.net = ReflectionUtils.newInstance(config.getTransportServerClass());
        this.net.init(
                config.getPort(),
                handler
        );

        this.encoder = ReflectionUtils.newInstance(config.getEncoderClass());
        this.decoder = ReflectionUtils.newInstance(config.getDecoderClass());

        this.serviceManager = new ServiceManager();
        this.serviceInvoker= new ServiceInvoker();
    }

    public <T> void register(Class<T> interfaceClass,T bean){
        serviceManager.register(interfaceClass,bean);
    }

    public void start(){
        this.net.start();
    }

    public void stop(){
        this.net.stop();
    }

    private RequestHandler handler = new RequestHandler() {
        public void onRequest(InputStream recive, OutputStream toReson) {
            Response resp  =  new Response();
            try {
                byte[] inBytes = IOUtils.readFully(recive,recive.available());
                Request request = decoder.decode(inBytes, Request.class);
                log.info("get request {}",request);

                ServiceInstance sis = serviceManager.lookup(request);
                Object ret = serviceInvoker.invoke(sis,request);
                resp.setData(ret);
            } catch (IOException e) {
                log.warn(e.getMessage(),e);
                resp.setData(1);
                resp.setMessage("RpcServer got error: "+e.getClass().getName()+" : "+e.getMessage());
            }finally {
                byte[] outBytes = encoder.encode(resp);
                try {
                    toReson.write(outBytes);
                    log.info("reponse client");
                } catch (IOException e) {
                    log.warn(e.getMessage());
                }
            }

        }
    };
}
